package com.springBoot.springBootSysCore.config;

import com.springBoot.springBootSysCore.common.utils.encoding.Encodes;
import com.springBoot.springBootSysCore.modules.services.SystemService;
import com.springBoot.springBootSysCore.modules.shiro.*;
import com.springBoot.springBootSysCore.modules.shiro.session.SessionStorageEvaluator;
import com.springBoot.springBootSysCore.modules.shiro.stateles.StatelessDefaultSubjectFactory;
import com.springBoot.springBootSysCore.modules.shiro.stateles.StatelessRealm;
import com.springBoot.springBootSysCore.modules.shiro.session.CacheSessionDAO;
import com.springBoot.springBootSysCore.modules.shiro.session.SessionManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SubjectDAO;
import org.apache.shiro.realm.Realm;
//import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.apache.shiro.web.servlet.SimpleCookie;
//import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
@EnableCaching
@EnableTransactionManagement
public class ShiroConfiguration implements EnvironmentAware {
	private Environment env;


	/*
     * 据shared与否的设置,Spring分别通过CacheManager.create()或new CacheManager()方式来创建一个ehcache基地.
     */
//	@Bean
//	public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
//		EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean ();
//		cacheManagerFactoryBean.setConfigLocation (new ClassPathResource("ehcache-local.xml"));
//		cacheManagerFactoryBean.setShared (true);
//		return cacheManagerFactoryBean;
//	}
//	/*
//	* ehcache 主要的管理器
//	*/
//	@Bean(name = "cacheManager")
//	public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){
//		return new EhCacheCacheManager (bean.getObject ());
//	}
	//	@Bean(name = "shiroCacheManager")
//	public EhCacheManager shiroCacheManager(net.sf.ehcache.CacheManager manager) {
//		EhCacheManager ehCacheManager = new EhCacheManager();
//		ehCacheManager.setCacheManager(manager);
//		return ehCacheManager;
//	}


	//(name = "shiroCacheManager") 将cacheManager注入到shiro中
	//否则会出现
	// Field cacheManager in com.springBoot.springBootSysCore.modules.shiro.session.CacheSessionDAO required a bean of type 'org.apache.shiro.cache.CacheManager' that could not be found.
	// Consider defining a bean of type 'org.apache.shiro.cache.CacheManager' in your configuration.
	@Bean(name = "shiroCacheManager")
	public CacheManager cacheManager(){
		EhCacheManager cacheManager = new EhCacheManager();
		cacheManager.setCacheManagerConfigFile("classpath:ehcache-local.xml");
		return cacheManager;
	}


	//凭证匹配器 加密  增加登录次数控制 --在realm中也可配置 如@PostConstruct
	@Bean
	public CredentialsMatcher credentialsMatcher(){
		RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager());
//		credentialsMatcher.setHashAlgorithmName("MD5");
//		credentialsMatcher.setHashIterations(2);
		credentialsMatcher.setHashAlgorithmName(SystemService.HASH_ALGORITHM);
		credentialsMatcher.setHashIterations(SystemService.HASH_INTERATIONS);
		credentialsMatcher.setStoredCredentialsHexEncoded(true);
		return credentialsMatcher;
	}

	@Bean("systemAuthorizingRealm")
	public SystemAuthorizingRealm systemAuthorizingRealm(){
		SystemAuthorizingRealm realm = new SystemAuthorizingRealm();
		realm.setCredentialsMatcher(credentialsMatcher());
		realm.setCachingEnabled(true);
		realm.setAuthorizationCachingEnabled(true);
		realm.setAuthorizationCacheName("authorizationCache");
		return realm;
	}
	@Bean("statelessRealm")
	public StatelessRealm statelessRealm(){
		StatelessRealm realm =  new StatelessRealm();
//		realm.setCredentialsMatcher(credentialsMatcher());
		return realm;
	}

//	@Bean
//	public SessionValidationScheduler sessionValidationScheduler(){
//		ExecutorServiceSessionValidationScheduler sessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();
//		sessionValidationScheduler.setInterval(3600000);
//		return sessionValidationScheduler;
//	}
//	@Bean
//	public SimpleCookie sessionIdCookie(){
//		SimpleCookie sessionIdCookie = new SimpleCookie();
//		sessionIdCookie.setName(env.getProperty("shiro.sessionId"));
//		sessionIdCookie.setHttpOnly(true);
//		sessionIdCookie.setMaxAge(-1);
//		sessionIdCookie.setDomain("");
//		sessionIdCookie.setPath("/");
//		return sessionIdCookie;
//	}
//	@Bean
//	public SessionDAO sessionDAO(){
//		EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
//		sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
//		sessionDAO.setSessionIdGenerator(new JavaUuidSessionIdGenerator());
//		return sessionDAO;
//	}
//	@Bean
//	public SessionManager sessionManager(CacheSessionDAO dao){
//		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//		sessionManager.setGlobalSessionTimeout(3600000);
//		sessionManager.setDeleteInvalidSessions(true);
//		sessionManager.setSessionValidationSchedulerEnabled(true);
//		sessionManager.setSessionValidationScheduler(sessionValidationScheduler());
//
////		sessionManager.setSessionDAO(sessionDAO());
//		sessionManager.setSessionDAO(dao);
//
//		sessionManager.setSessionIdCookieEnabled(true);
//		sessionManager.setSessionIdCookie(sessionIdCookie());
//		return sessionManager;
//	}

	@Bean
	public  CacheSessionDAO cacheSessionDAO(){
		return new CacheSessionDAO();
	}

	@Bean(name = "sessionManager")
	public SessionManager sessionManager(CacheSessionDAO dao) {
		com.springBoot.springBootSysCore.modules.shiro.session.SessionManager sessionManager = new com.springBoot.springBootSysCore.modules.shiro.session.SessionManager();
		sessionManager.setSessionDAO(dao);
		sessionManager.setGlobalSessionTimeout(86400000);
		sessionManager.setDeleteInvalidSessions(true);
		sessionManager.setSessionValidationInterval(1800000);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setSessionIdCookie(new SimpleCookie(env.getProperty("shiro.sessionId")));
		sessionManager.setSessionIdCookieEnabled(true);
		return sessionManager;
	}
	/**
	 * cookie对象;
	 * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
	 * @return
	 */
	@Bean
	public SimpleCookie rememberMeCookie(){
		//System.out.println("ShiroConfiguration.rememberMeCookie()");
		//这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		//<!-- 记住我cookie生效时间30天 ,单位秒;-->
		simpleCookie.setMaxAge(259200);
		return simpleCookie;
	}
	/**
	 * cookie管理对象;
	 * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
	 * @return
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager(){
		//System.out.println("ShiroConfiguration.rememberMeManager()");
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
		cookieRememberMeManager.setCipherKey(Encodes.decodeBase64("2AvVhdsgUs0FSA3SDFAdag=="));
		return cookieRememberMeManager;
	}

	@Bean
	public  FirstSuccessfulStrategy firstSuccessfulStrategy(){
		return new FirstSuccessfulStrategy();
	}

	@Bean
	public DefautModularRealm defautModularRealm(FirstSuccessfulStrategy firstSuccessfulStrategy,
												 SystemAuthorizingRealm systemAuthorizingRealm,
												 StatelessRealm statelessRealm){
		DefautModularRealm defautModularRealm = new DefautModularRealm();

//        　　AuthenticationStrategy接口的默认实现：
//        　　FirstSuccessfulStrategy：只要有一个Realm 验证成功即可，只返回第一个Realm 身份验证成功的认证信息，其他的忽略；
//        　　AtLeastOneSuccessfulStrategy：只要有一个Realm验证成功即可，和FirstSuccessfulStrategy不同，将返回所有Realm身份验证成功的认证信息；
//        　　AllSuccessfulStrategy：所有Realm验证成功才算成功，且返回所有Realm身份验证成功的认证信息，如果有一个失败就失败了。
//        　　ModularRealmAuthenticator默认是AtLeastOneSuccessfulStrategy策略

		defautModularRealm.setAuthenticationStrategy(firstSuccessfulStrategy);
		Map<String,Object> map = new HashMap<>();
		map.put("systemAuthorizingRealm",systemAuthorizingRealm);
		map.put("statelessRealm",statelessRealm);
		defautModularRealm.setDefinedRealms(map);
		return defautModularRealm;
	}
	@Bean
	public StatelessDefaultSubjectFactory statelessDefaultSubjectFactory(){
		return  new StatelessDefaultSubjectFactory();
	}

	@Bean
	public DefaultWebSecurityManager securityManager(SessionManager sessionManager,
													 StatelessDefaultSubjectFactory statelessDefaultSubjectFactory,
													 DefautModularRealm defautModularRealm,
													 SystemAuthorizingRealm systemAuthorizingRealm,
													 StatelessRealm statelessRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

//		DefaultSubjectDAO dao = (DefaultSubjectDAO) securityManager.getSubjectDAO();
//		dao.setSessionStorageEvaluator(new SessionStorageEvaluator());
//		securityManager.setSubjectDAO(dao);

		ArrayList<Realm> realms = new ArrayList<>();
		realms.add(systemAuthorizingRealm);
		realms.add(statelessRealm);
		//		securityManager.setRealm(userRealm());
		securityManager.setRealms(realms);
		securityManager.setAuthenticator(defautModularRealm);//认证
		realms.clear();
		realms.add(systemAuthorizingRealm);
		DefautModularRealmAuthorizer defautModularRealmAuthorizer = new DefautModularRealmAuthorizer();
		defautModularRealmAuthorizer.setRealms(realms);
		securityManager.setAuthorizer(defautModularRealmAuthorizer);//授权 - 仅仅进行后台授权--暂未扩展 此处不能放在认证之前

//		securityManager.setSessionManager(sessionManager());
		securityManager.setSessionManager(sessionManager);
		securityManager.setCacheManager(cacheManager());
		securityManager.setSubjectFactory(statelessDefaultSubjectFactory);
		SecurityUtils.setSecurityManager(securityManager);
		//不设置 默认一年
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}
	@Bean
	public FormAuthenticationFilter formAuthenticationFilter(){
		return  new FormAuthenticationFilter();
	}

	@Bean
	public CustomDefaultFilterChainManager filterChainManager(
			BasicHttpAuthenticationFilter basicHttpAuthenticationFilter,
			FormAuthenticationFilter formAuthenticationFilter){
		CustomDefaultFilterChainManager filterChainManager = new CustomDefaultFilterChainManager();
		filterChainManager.setLoginUrl(env.getProperty("adminPath")+env.getProperty("shiro.loginUrl"));
		filterChainManager.setSuccessUrl(env.getProperty("adminPath")+env.getProperty("shiro.successUrl"));
		filterChainManager.setUnauthorizedUrl(env.getProperty("adminPath")+env.getProperty("shiro.unauthorizedUrl"));
		Map<String, Filter> customFilters = new HashMap<String, Filter>();

		customFilters.put("basic", basicHttpAuthenticationFilter);
		customFilters.put("authc", formAuthenticationFilter);

		filterChainManager.setCustomFilters(customFilters);
		//注册拦截器链
		filterChainManager.setFiltersStr(env.getProperty("shiro.filters"));


		Map<String, String> section = new LinkedHashMap<String,String>();

		section.put("/static/**", "anon");
		section.put("/validateCodeServlet", "anon");
		section.put("/userfiles/**","anon");
		section.put("/error","anon");
		/*section.put(env.getProperty("apiPath")+"/login*//**", "anon");*/

		section.put(env.getProperty("adminPath")+"/logout", "logout");
		section.put(env.getProperty("adminPath")+"/login", "authc");
		section.put(env.getProperty("adminPath")+"/basic", "basic");

		//注册拦截器链拦截地址
		String definitions = env.getProperty("shiro.chain.definitions");
		if(StringUtils.isNotBlank(definitions)){
			for (String st : definitions.split(";")) {
				String[] con = st.split("=");
				section.put(con[0], con[1]);
			}
		}
		section.put(env.getProperty("adminPath")+"/**", "user");

		filterChainManager.setDefaultFilterChainDefinitions(section);
		return filterChainManager;
	}
	@Bean
	public CustomPathMatchingFilterChainResolver filterChainResolver(CustomDefaultFilterChainManager filterChainManager){
		CustomPathMatchingFilterChainResolver filterChainResolver = new CustomPathMatchingFilterChainResolver();
		filterChainResolver.setFilterChainManager(filterChainManager);
		return filterChainResolver;
	}
	//自定义的拦截器
//	@Bean(name="shiroFilter")
//	public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
//		MyShiroFilterFactoryBean shiroFilter = new MyShiroFilterFactoryBean();
//		shiroFilter.setSecurityManager(securityManager);
//		return shiroFilter;
//	}
	/**
	 * 全局的环境变量的设置
	 * shiro的拦截
	 * @return
	 */
//	@Bean(name = "shiroFilterChainDefinitions")
//	public String shiroFilterChainDefinitions(Environment environment, @Value("${adminPath}") String adminPath) {
////        Global.resolver = new RelaxedPropertyResolver(environment);
//		String string = "/static/** = anon\n";
//		string += "/userfiles/** = anon\n";
//		string += adminPath + "/basic = basic\n";
//		string += adminPath + "/login = authc\n";
//		string += adminPath + "/logout = logout\n";
//		string += adminPath + "/** = user\n";
//		string += "/ReportServer/** = user";
//		return string;
//	}
	@Bean(name = "basicHttpAuthenticationFilter")
	public BasicHttpAuthenticationFilter casFilter() {
		BasicHttpAuthenticationFilter basicHttpAuthenticationFilter = new BasicHttpAuthenticationFilter();
		basicHttpAuthenticationFilter.setLoginUrl(env.getProperty("adminPath") + "/login");
		return basicHttpAuthenticationFilter;
	}


	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(
//			@Value("${adminPath}")String adminPath,
//			BasicHttpAuthenticationFilter basicHttpAuthenticationFilter,
//			FormAuthenticationFilter formAuthenticationFilter,
			DefaultWebSecurityManager securityManager
//			CustomPathMatchingFilterChainResolver filterChainResolver;
//			,
//			@Qualifier("shiroFilterChainDefinitions") String shiroFilterChainDefinitions
		) {
//		Map<String, Filter> filters = new HashMap<>();
//		filters.put("basic", basicHttpAuthenticationFilter);
//		filters.put("authc", formAuthenticationFilter);
		MyShiroFilterFactoryBean shiroFilter = new MyShiroFilterFactoryBean();
		shiroFilter.setSecurityManager(securityManager);
//		shiroFilter.setFilters(filters);
//		ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//		bean.setFilters(filters);
//		bean.setSecurityManager(securityManager);
//		bean.setLoginUrl(adminPath + "/login");
//		bean.setSuccessUrl(adminPath + "?login");
//		bean.setFilterChainDefinitions(shiroFilterChainDefinitions);
		return shiroFilter;
	}

	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
		return new LifecycleBeanPostProcessor();
	}
	@Bean
	@DependsOn("lifecycleBeanPostProcessor")
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
		return defaultAdvisorAutoProxyCreator;
	}
	/**
	 * ShiroDialect，为了在thymeleaf里使用shiro的标签的bean
	 * @return
	 */
//	@Bean
//	public ShiroDialect shiroDialect(){
//		return new ShiroDialect();
//	}
	@Override
	public void setEnvironment(Environment environment) {
		this.env = environment;
	}

//	<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
//        <property name="targetObject" ref="shiroFilter"/>
//        <property name="targetMethod" value="setFilterChainResolver"/>
//        <property name="arguments" ref="filterChainResolver"/>
//    </bean>
//此处处理方式
//	MyShiroFilterFactoryBean shiroFilter = new MyShiroFilterFactoryBean();
//		shiroFilter.setSecurityManager(securityManager);
//		shiroFilter.setFilters(filters);

	//在用spring管理我们的类的时候有时候希望有些属性值是来源于一些配置文件，系统属性，或者一些方法调用的结果，
	//对于前两种使用方式可以使用spring的PropertyPlaceholderConfigurer类来注入，
	//对于后一种则可以使用org.springframework.beans.factory.config.MethodInvokingFactoryBean类来生成需要注入的bean的属性
	//通过MethodInvokingFactory Bean类，可注入方法返回值。
	// MethodInvokingFactoryBean用来获得某个方法的返回值，该方法既可以是静态方法，也可以是实例方法。
	// 该方法的返回值可以注入bean实例属性，也可以直接定义成bean实例。
//	@Bean
//	public MethodInvokingFactoryBean methodInvokingFactoryBean() {
//		MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
//		bean.setArguments(new Object[] { resolver });
//		bean.setStaticMethod("java.lang.System.setProperties");
//		Properties props = System.getProperties();
//		props.setProperty("http.proxyHost", "proxy.yqu.com");
//		props.setProperty("http.proxyPort", "80");
//		bean.setArguments(new Object[] { props });
//		return bean;
//	}
	//MethodInvokingBean是MethodInvokingFactoryBean的父类，更为简单。跟MethodInvokingFactoryBean相比，不会对容器返回任何值。
//	@Bean
//	public MethodInvokingBean methodInvokingBean() {
//		MethodInvokingBean mBean = new MethodInvokingBean();
//		mBean.setStaticMethod("com.yqu.methodinvoker.Application.finish");
//		mBean.setArguments(
//				new String[] {
//						"--url", "jdbc:hsqldb:mem:testdb",
//						"--user", "sa", "--password", ""
//				}
//		);
//		return mBean;
//	}
//	public static void finish(String[] args) {
//		log.info("finish "+Arrays.toString(args));
//	}
//	private String getStacks() {
//		StringBuilder sb = new StringBuilder();
//		StackTraceElement[] elements = Thread.currentThread().getStackTrace();
//		sb.append("========================\n");
//		for (int i = 0; i < elements.length; i++) {
//			sb.append(elements[i]).append("\n");
//		}
//		return sb.toString();
//	}

	@Bean
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
		filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
		// 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
		filterRegistration.addInitParameter("targetFilterLifecycle", "true");
		filterRegistration.setEnabled(true);
		filterRegistration.addUrlPatterns("/*");// 可以自己灵活的定义很多，避免一些根本不需要被Shiro处理的请求被包含进来
		filterRegistration.setOrder(1);
		return filterRegistration;
	}
//1.LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类，负责org.apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。主要是AuthorizingRealm类的子类，以及EhCacheManager类。
//2.HashedCredentialsMatcher，这个类是为了对密码进行编码的，防止密码在数据库里明码保存，当然在登陆认证的生活，这个类也负责对form里输入的密码进行编码。
//3.ShiroRealm，这是个自定义的认证类，继承自AuthorizingRealm，负责用户的认证和权限的处理，可以参考JdbcRealm的实现。
//4.EhCacheManager，缓存管理，用户登陆成功后，把用户信息和权限信息缓存起来，然后每次用户请求时，放入用户的session中，如果不设置这个bean，每个请求都会查询一次数据库。
//5.SecurityManager，权限管理，这个类组合了登陆，登出，权限，session的处理，是个比较重要的类。
//6.ShiroFilterFactoryBean，是个factorybean，为了生成ShiroFilter。它主要保持了三项数据，securityManager，filters，filterChainDefinitionManager。
//7.DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
//8.AuthorizationAttributeSourceAdvisor，shiro里实现的Advisor类，内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。*/

}
